Skip to content

getModuleGroup removal using common class-loaders for instrumentation modules.#18859

Open
SylvainJuge wants to merge 18 commits into
open-telemetry:mainfrom
SylvainJuge:module-group-2
Open

getModuleGroup removal using common class-loaders for instrumentation modules.#18859
SylvainJuge wants to merge 18 commits into
open-telemetry:mainfrom
SylvainJuge:module-group-2

Conversation

@SylvainJuge

@SylvainJuge SylvainJuge commented May 27, 2026

Copy link
Copy Markdown
Contributor

Alternative to #18707 (and #18830) by using a single shared classloader for instrumentation modules.

Fixes #17720
Fixes #18824 (no longer needed, we will try to find a simpler approach to inject classes).

Description

  • internal instrumentation modules: all loaded in a single InstrumentationModuleClassLoader instance, hence we don't get any issue with shared classes.
  • extensions instrumentation modules: each extension is loaded in an isolated InstrumentationModuleClassLoader

Hypotheses to verify

  • we should not get any shared class issues as they are loaded exactly once in the same CL (tests should pass as they currently do)
    • answer: this works as expected, the -PdenyUnsafe=true which is using an extension triggered a few issues that are now fixed.
  • we loose the isolation between instrumentation modules
    • for internal instrumentation this is probably not an issue as there is no overlap in classes.
    • for extensions, they are currently isolated by default, so we need to evaluate how likely it could cause issues.
      • answer: extensions are still isolated with their own InstrumentationModuleClassLoader, thus they remain independant from the internal instrumentation.

@SylvainJuge SylvainJuge self-assigned this May 28, 2026
@SylvainJuge SylvainJuge changed the title [wip] getModuleGroup removal using single CL for all modules getModuleGroup removal using common class-loaders for instrumentation modules. May 28, 2026
@SylvainJuge SylvainJuge marked this pull request as ready for review May 28, 2026 13:41
@SylvainJuge SylvainJuge requested a review from a team as a code owner May 28, 2026 13:41
Copilot AI review requested due to automatic review settings May 28, 2026 13:41
@SylvainJuge SylvainJuge requested a review from laurit May 28, 2026 13:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes reliance on ExperimentalInstrumentationModule.getModuleGroup() by changing invokedynamic (indy) helper-class loading to use a shared InstrumentationModuleClassLoader: one per instrumented application class loader for internal instrumentations, and one per (extension class loader, instrumented class loader) pair for extensions. It also deprecates getModuleGroup() and removes now-unneeded overrides across many instrumentation modules.

Changes:

  • Reworked IndyModuleRegistry to stop grouping by getModuleGroup() and instead select shared class loaders based on whether a module is internal vs extension-loaded.
  • Deprecated ExperimentalInstrumentationModule.getModuleGroup() and removed getModuleGroup() overrides / interface implementations where they were only used for grouping.
  • Improved the InstrumentationModuleClassLoader.installModule(...) exception message to include the actual module class loader.

Reviewed changes

Copilot reviewed 63 out of 63 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/InstrumentationModuleClassLoader.java Improves error message when a module is installed from an unexpected class loader.
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyModuleRegistry.java Replaces module-group-based loader selection with internal-vs-extension shared loader logic.
javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/internal/ExperimentalInstrumentationModule.java Deprecates getModuleGroup() (still present) and documents new loader behavior.
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/SpringWebMvcInstrumentationModule.java Removes getModuleGroup() override (servlet grouping).
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/springweb/SpringWebInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override.
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/SpringWebMvcInstrumentationModule.java Removes getModuleGroup() override (servlet grouping).
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/springweb/SpringWebInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override.
instrumentation/spring/spring-webflux/spring-webflux-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/webflux/v5_0/server/reactornetty/ReactorNettyInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/spring/spring-security-config-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/security/config/v6_0/servlet/SpringSecurityConfigServletInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (servlet grouping).
instrumentation/spring/spring-cloud-gateway/spring-cloud-gateway-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/cloud/gateway/v2_0/GatewayInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/spring/spring-cloud-aws-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/cloud/aws/v3_0/SpringAwsSqsInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (aws-sdk-v2 grouping).
instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/Servlet3InstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (servlet grouping).
instrumentation/reactor/reactor-3.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactor/v3_4/operator/ContextPropagationOperator34InstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/reactor/reactor-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactor/v3_1/ReactorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/reactor/reactor-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/reactor/v3_1/operator/ContextPropagationOperatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/ratpack/ratpack-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_4/RatpackInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/tapir/TapirPekkoHttpServerRouteInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (pekko-server grouping).
instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/route/PekkoHttpServerRouteInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (pekko-server grouping).
instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/PekkoHttpServerInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (pekko-server grouping).
instrumentation/opentelemetry-instrumentation-api/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationapi/InstrumentationApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-extension-kotlin-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetry/extension/kotlin/v1_0/ContextExtensionInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.59/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_59/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.57/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_57/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.56/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_56/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.50/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_50/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.50/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_50/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.47/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_47/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.42/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_42/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.42/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_42/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.40/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_40/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_4/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.38/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_38/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.37/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_37/incubator/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.32/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_32/incubator/OpenTelemetryApiIncubatorInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.31/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_31/incubator/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.27/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_27/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/OpenTelemetryApiInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/TestInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (test module grouping).
instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/OpenTelemetryApiInstrumentationModule.java Removes getModuleGroup() override (api-bridge grouping).
instrumentation/netty/netty-4.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_1/NettyInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/netty/netty-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v4_0/NettyInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/netty/netty-3.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/v3_8/NettyInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (netty grouping).
instrumentation/kotlinx-coroutines/kotlinx-coroutines-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/kotlinxcoroutines/v1_0/KotlinCoroutinesInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (api-bridge grouping).
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/jersey/v2_0/JerseyInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (servlet grouping).
instrumentation/hibernate/hibernate-procedure-call-4.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/procedure/call/v4_3/HibernateInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (hibernate grouping).
instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/HibernateInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (hibernate grouping).
instrumentation/hibernate/hibernate-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v4_0/HibernateInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (hibernate grouping).
instrumentation/hibernate/hibernate-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v3_3/HibernateInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (hibernate grouping).
instrumentation/finagle-http-23.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/finaglehttp/v23_11/FinagleHttpInstrumentationModule.java Removes getModuleGroup() override (netty grouping).
instrumentation/elasticsearch/elasticsearch-rest-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/rest/v7_0/ElasticsearchRest7InstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (elasticsearch grouping).
instrumentation/elasticsearch/elasticsearch-api-client-7.16/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/elasticsearch/api/client/v7_16/ElasticsearchApiClientInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (elasticsearch grouping).
instrumentation/couchbase/couchbase-2.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_6/CouchbaseInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (couchbase grouping).
instrumentation/couchbase/couchbase-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v2_0/CouchbaseInstrumentationModule.java Removes getModuleGroup() override (couchbase grouping).
instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/AwsSdkInstrumentationModule.java Moves ExperimentalInstrumentationModule implementation to concrete module (still uses exposed helpers).
instrumentation/aws-sdk/aws-sdk-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v2_2/AbstractAwsSdkInstrumentationModule.java Drops ExperimentalInstrumentationModule and getModuleGroup() override from abstract base.
instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSdkInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (aws-sdk grouping).
instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AbstractAwsSdkInstrumentationModule.java Drops ExperimentalInstrumentationModule and getModuleGroup() override from abstract base.
instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/v10_0/server/route/AkkaHttpServerRouteInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (akka-http grouping).
instrumentation/akka/akka-http-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/akkahttp/v10_0/server/AkkaHttpServerInstrumentationModule.java Drops ExperimentalInstrumentationModule + getModuleGroup() override (akka-http grouping).

@JonasKunz JonasKunz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me, looks a lot simpler than the previous approach.

// internal instrumentation is using one CL per instrumented CL.
return instrumentationClassLoaders.get(instrumentedClassLoader);
}
// extension module needs to use a common CL per extension and instrumented CL.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need special handling for extensions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, running ./gradlew :instrumentation:jboss-logmanager:jboss-logmanager-appender-1.1:javaagent:test -PtestIndy=true -PdenyUnsafe=true will produce errors like below because the InstrumentationModule is being loaded by the AgentClassLoader:

java.lang.IllegalArgumentException: io.opentelemetry.javaagent.testing.instrumentation.DenyUnsafeInstrumentationModule is not loaded by io.opentelemetry.javaagent.bootstrap.AgentClassLoader@5cb0d902 but is loaded by io.opentelemetry.javaagent.tooling.ExtensionClassLoader@2f410acf
	at io.opentelemetry.javaagent.tooling.instrumentation.indy.InstrumentationModuleClassLoader.installModule(InstrumentationModuleClassLoader.java:154)
	at io.opentelemetry.javaagent.tooling.instrumentation.indy.InstrumentationModuleClassLoader.installModule(InstrumentationModuleClassLoader.java:147)
	at io.opentelemetry.javaagent.tooling.instrumentation.indy.IndyModuleRegistry.initializeModuleLoaderForClassLoader(IndyModuleRegistry.java:160)

Previously, the extensions were isolated on a per InstrumentationModule class name, so the isolation is still less strict than it used to be when using indy.

@trask

trask commented Jun 19, 2026

Copy link
Copy Markdown
Member
  • internal instrumentation modules: all loaded in a single InstrumentationModuleClassLoader instance, hence we don't get any issue with shared classes.
  • extensions instrumentation modules: each extension is loaded in an isolated InstrumentationModuleClassLoader

this makes sense to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explicit class-loading target for instrumentation classes invokedynamic : remove/replace getModuleGroup

5 participants